package org.mobai.flow.strategy.impl;

import cn.hutool.core.util.ObjectUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.camunda.bpm.model.bpmn.builder.AbstractFlowNodeBuilder;
import org.camunda.bpm.model.bpmn.builder.ExclusiveGatewayBuilder;
import org.mobai.flow.constant.CamundaConstant;
import org.mobai.flow.enums.OperatorEnum;
import org.mobai.flow.pojo.bpmn.Condition;
import org.mobai.flow.pojo.bpmn.Conditions;
import org.mobai.flow.pojo.bpmn.Operations;
import org.mobai.flow.pojo.bpmn.node.ConditionNode;
import org.mobai.flow.pojo.bpmn.node.ExclusiveGateWay;
import org.mobai.flow.strategy.BpmnStrategy;
import org.mobai.flow.strategy.context.BpmnBuildContext;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 构建排他网关
 *
 * @author hyk
 */
@AllArgsConstructor
@Service
@Slf4j
public class ExclusiveGatewayBpmnImpl implements BpmnStrategy<ExclusiveGateWay> {
    private BpmnBuildContext bpmnBuildContext;

    /**
     * 构建一个独占网关及其子节点
     * 此方法负责根据提供的独占网关对象初始化一个独占网关构建器，设置其ID和名称，
     * 并根据独占网关的子节点条件构建相应的流程片段
     *
     * @param flowNodeBuilder  流程节点构建器，用于初始化独占网关构建器
     * @param exclusiveGateWay 独占网关对象，包含网关的ID、名称及其子节点条件
     * @return 返回构建后的独占网关构建器或其子节点构建器
     */
    @Override
    public AbstractFlowNodeBuilder build(AbstractFlowNodeBuilder flowNodeBuilder, ExclusiveGateWay exclusiveGateWay, Map<String, String> nodeMap) {
        // 初始化独占网关构建器
        ExclusiveGatewayBuilder exclusiveGatewayBuilder = flowNodeBuilder.exclusiveGateway();
        exclusiveGatewayBuilder.id(exclusiveGateWay.getId());
        exclusiveGatewayBuilder.name(exclusiveGateWay.getName());
        List<ConditionNode> children = exclusiveGateWay.getChildren();
        ExclusiveGatewayBuilder endexclusiveGatewayBuilder = null;
        for (ConditionNode child : children) {
            // 构建连接条件
            String conditions = buildConditions(child.getConditions());
            if (ObjectUtil.isNotEmpty(conditions)) {
                exclusiveGatewayBuilder.condition(child.getName(), String.format(CamundaConstant.EL_EXPRESSION, conditions));
            }
            // 构建子节点
            AbstractFlowNodeBuilder abstractFlowNodeBuilder = exclusiveGatewayBuilder;
            if (ObjectUtil.isNotNull(child.getChild())) {
                Operations childOperations = child.getOperations();
                String backNodeId = child.getPid();
                if (ObjectUtil.isNotEmpty(childOperations)) {
                    backNodeId = childOperations.getBackNodeId();
                }
                String finalBackNodeId = backNodeId;
                Optional.ofNullable(child.getChild().getOperations()).ifPresentOrElse(operations -> {
                    operations.setBackNodeId(finalBackNodeId);
                }, () -> {
                    child.getChild().setOperations(new Operations() {{
                        setBackNodeId(finalBackNodeId);
                    }});
                });
                abstractFlowNodeBuilder = bpmnBuildContext.buildBpmn(exclusiveGatewayBuilder, child.getChild(),nodeMap);
            }

            // 构建结束节点
            if (endexclusiveGatewayBuilder == null) {
                endexclusiveGatewayBuilder = abstractFlowNodeBuilder.exclusiveGateway();
            } else {
                abstractFlowNodeBuilder.connectTo(endexclusiveGatewayBuilder.getElement().getId());
            }
        }
        if (ObjectUtil.isNotNull(exclusiveGateWay.getChild())) {
            return bpmnBuildContext.buildBpmn(endexclusiveGatewayBuilder, exclusiveGateWay.getChild(),nodeMap);
        }

        if (endexclusiveGatewayBuilder == null) {
            return exclusiveGatewayBuilder;
        }
        return endexclusiveGatewayBuilder;
    }

    /**
     * 构建条件
     * 根据给定的Conditions对象构建Camunda流程引擎的条件字符串
     *
     * @param conditions 包含条件和条件组的Conditions对象
     * @return 构建的条件字符串，如果构建失败或条件无效，则返回null
     */
    private String buildConditions(Conditions conditions) {
        // 参数校验
        if (conditions == null) {
            return null;
        }

        // 设置camunda条件字段
        // 获取操作符
        String operator = conditions.getOperator();
        OperatorEnum operatorEnum = OperatorEnum.getByType(operator);
        if (operatorEnum == null) {
            return null;
        }

        // 构建嵌套条件
        List<Conditions> conditionsList = conditions.getGroups();
        String nestedConditions = "";
        if (conditionsList != null && !conditionsList.isEmpty()) {
            nestedConditions = "(" + conditionsList.stream()
                    .map(this::buildConditions)
                    .filter(ObjectUtil::isNotNull)
                    .collect(Collectors.joining(operatorEnum.getValue()))
                    + ")";
        }

        // 构建普通条件
        List<Condition> conditionList = conditions.getConditions();
        String simpleConditions = conditionList.stream()
                .map(this::buildCondition)
                .filter(ObjectUtil::isNotNull)
                .collect(Collectors.joining(operatorEnum.getValue()));

        // 合并条件
        return simpleConditions + nestedConditions;
    }

    /**
     * 根据给定的条件对象构建条件字符串
     * 该方法主要用于组装条件，根据条件对象中的字段、操作符和值生成符合camunda的流转条件的字符串
     *
     * @param condition 条件对象，包含字段名、操作符和条件值
     * @return 返回组装好的条件字符串，如果操作符不合法，则返回null
     */
    private String buildCondition(Condition condition) {
        if (condition == null) {
            // 记录日志，这里假设使用slf4j
            log.error("Condition object is null.");
            return null;
        }

        String operator = condition.getOperator();
        OperatorEnum operatorEnum = OperatorEnum.getByType(operator);
        if (ObjectUtil.isNull(operatorEnum)) {
            return null;
        }

        String field = condition.getField();
        Object value = condition.getValue();

        if (field == null || value == null) {
            // 记录日志，这里假设使用slf4j
            log.error("Field or value is null in condition object.");
            return null;
        }

        StringBuilder conditionStr = new StringBuilder();
        conditionStr.append(field).append(operatorEnum.getValue());

        if (value instanceof Number) {
            conditionStr.append(value);
        } else {
            conditionStr.append("'").append(value).append("'");
        }

        return conditionStr.toString();
    }

}
